package com.yayako.main.drivers;

import com.yayako.main.entity.Word;
import com.yayako.main.struc.DFA;
import com.yayako.main.struc.MinimizeDFA;
import com.yayako.main.struc.NFA;
import com.yayako.main.utils.FileUtil;

import java.util.HashMap;
import java.util.List;

/**
 * 驱动器
 *
 * @author cyy
 */
public class Driver {
    /** 文件输出路径 */
    private static final String TOKEN_TXT_PATH =
            "D:\\Projects\\LexicalAnalyzer\\src\\com\\yayako\\main\\com.yayako.out\\out_token.txt";
    /** 预先缓存kw、ks、relation、const的map */
    private static final HashMap<String, Word> MAP;
    /** 检错日志 */
    private static StringBuilder logger;
    /** 记号流 */
    private static StringBuilder res;

    static {
        MAP = new HashMap<>();

        MAP.put(">", new Word(">", Word.Type.relation));
        MAP.put(">=", new Word(">=", Word.Type.relation));
        MAP.put("<", new Word("<", Word.Type.relation));
        MAP.put("<=", new Word("<=", Word.Type.relation));
        MAP.put("==", new Word("==", Word.Type.relation));
        MAP.put("!=", new Word("!=", Word.Type.relation));
        MAP.put("=", new Word("=", Word.Type.relation));

        MAP.put("(", new Word("(", Word.Type.ks));
        MAP.put(")", new Word(")", Word.Type.ks));
        MAP.put(":", new Word(":", Word.Type.ks));
        MAP.put(";", new Word(";", Word.Type.ks));
        MAP.put("{", new Word("{", Word.Type.ks));
        MAP.put("}", new Word("}", Word.Type.ks));
        MAP.put("+", new Word("+", Word.Type.ks));
        MAP.put("-", new Word("-", Word.Type.ks));
        MAP.put("*", new Word("*", Word.Type.ks));
        MAP.put("/", new Word("/", Word.Type.ks));
        MAP.put(",", new Word(",", Word.Type.ks));
        MAP.put("?", new Word(",", Word.Type.ks));
        MAP.put("\"", new Word("\"", Word.Type.ks));
        MAP.put("\'", new Word("\'", Word.Type.ks));

        MAP.put("const", new Word("const", Word.Type.Const));
    }

    /**
     * 源程序==>记号流
     *
     * @param lines 源程序
     */
    public static void transform(List<String> lines) {
        res = new StringBuilder("记号类型\t单词\n");
        logger = new StringBuilder("\n");
        for (int line = 1; line <= lines.size(); ++line) {
            String content = lines.get(line - 1);
            int n = content.length();
            char[] chars = content.toCharArray();
            int start = 0;
            String s;
            char cur;
            boolean literal = false;

            for (int i = 0; i < n; ++i) {

                cur = chars[i];
                s = String.valueOf(cur);

                if (literal && cur != '"') {
                    continue;
                }

                if (cur == '"') { // literal
                    if (literal) {
                        res.append(new Word(content.substring(start, i), Word.Type.literal));
                        literal = false;
                    } else {
                        literal = true;
                    }
                    start = i + 1;
                    res.append(MAP.get(s));

                } else if (cur == '=' || cur == '>' || cur == '<') { // relation
                    if (i == 0 || chars[i - 1] != ' ') {
                        analyze(content.substring(start, i), line, start);
                    }
                    if (i < n - 1 && chars[i + 1] == '=') {
                        res.append(MAP.get(content.substring(i, i + 2)));
                        ++i; // 跳过 '='
                    } else {
                        res.append(MAP.get(s));
                    }
                    start = i + 1;

                } else if (MAP.containsKey(s)) { // ks
                    if (i == 0 || chars[i - 1] != ' ') {
                        analyze(content.substring(start, i), line, start);
                    }
                    res.append(MAP.get(s));
                    start = i + 1;

                } else if (cur == ' ' && i > 0 && chars[i - 1] != ' ') { // 'a '
                    analyze(content.substring(start, i), line, start);
                } else if (cur != ' ' && i > 0 && chars[i - 1] == ' ') { // ' a'
                    start = i;
                }
            }

            // 非法字面量，未以 '"' 结尾
            if (literal) {
                logger.append("Error:(")
                        .append(line)
                        .append(", ")
                        .append(n)
                        .append(") java: 未结束的literal")
                        .append('\n');
                res.append(new Word(content.substring(start, n), Word.Type.error));
            }

            // 可能存在最后一个遗漏的非分隔符单词
            if (!MAP.containsKey(String.valueOf(chars[n - 1]))) {
                analyze(content.substring(start, n), line, start);
            }
        }

        FileUtil.write2Txt(TOKEN_TXT_PATH, '\n' + res.append(logger).toString(), true);
    }

    /**
     * 单词识别
     *
     * @param word 对应单词
     * @param line 单词所在行
     * @param i 单词开始下标
     */
    private static void analyze(String word, int line, int i) {
        if (word.length() < 1) {
            return;
        }
        char c = word.charAt(0);
        if ('0' <= c && c <= '9') { // num
            isNum(word, line, i);
        } else {
            if (isKw(word)) { // kw
                return;
            }
            isId(word, line, i); // id
        }
    }

    /**
     * 辨别id
     *
     * @param word 对应单词
     * @param line 单词所在行
     * @param i 单词开始下标
     */
    private static void isId(String word, int line, int i) {
        if (IdRecognizer.isId(word)) {
            res.append(new Word(word, Word.Type.id));
        } else {
            logger.append("Error:(")
                    .append(line)
                    .append(", ")
                    .append(i)
                    .append(") java: 非法的id")
                    .append('\n');
            res.append(new Word(word, Word.Type.error));
        }
    }

    /**
     * 辨别kw
     *
     * @param word 对应单词
     */
    private static boolean isKw(String word) {
        if (KwRecognizer.isKw(word)) {
            res.append(new Word(word, Word.Type.kw));
            return true;
        }
        return false;
    }

    /**
     * 辨别num
     *
     * @param word 对应单词
     * @param line 单词所在行
     * @param i 单词开始下标
     */
    private static void isNum(String word, int line, int i) {
        if (NumRecognizer.isNum(word)) {
            res.append(new Word(word, Word.Type.num));
        } else {
            logger.append("Error:(")
                    .append(line)
                    .append(", ")
                    .append(i)
                    .append(") java: 非法的num")
                    .append('\n');
            res.append(new Word(word, Word.Type.error));
        }
    }

    /**
     * 将结果写入txt
     *
     * @param nfa nfa
     * @param dfa dfa
     * @param minimizeDFA mdfa
     */
    private static void writeRes2TXT(NFA nfa, DFA dfa, MinimizeDFA minimizeDFA) {
        FileUtil.write2Txt(TOKEN_TXT_PATH, nfa.toString(), true);
        FileUtil.write2Txt(TOKEN_TXT_PATH, dfa.toString(), true);
        FileUtil.write2Txt(TOKEN_TXT_PATH, minimizeDFA.toString(), true);
    }

    /** num识别器 */
    private static class NumRecognizer {
        private static final String NUM =
                "(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*(ε|.(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*)(ε|E(+|-|ε)(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*)";
        private static MinimizeDFA minimizeDFA;

        static {
            NFA nfa = new NFA(NUM);
            DFA dfa = new DFA(nfa);
            minimizeDFA = new MinimizeDFA(dfa);
            writeRes2TXT(nfa, dfa, minimizeDFA);
        }

        private NumRecognizer() {}

        static boolean isNum(String word) {
            return minimizeDFA.ifCanReachFinal(word);
        }
    }

    /** id识别器 */
    public static class IdRecognizer {
        private static final String ID =
                "(_|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z)(_|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9)*";
        private static MinimizeDFA minimizeDFA;

        static {
            NFA nfa = new NFA(ID);
            DFA dfa = new DFA(nfa);
            minimizeDFA = new MinimizeDFA(dfa);
            writeRes2TXT(nfa, dfa, minimizeDFA);
        }

        private IdRecognizer() {}

        static boolean isId(String word) {
            return minimizeDFA.ifCanReachFinal(word);
        }
    }

    /** relation识别器 */
    public static class RelationRecognizer {
        private static final String RELATION = ">|<|>=|<=|==";
        private static MinimizeDFA minimizeDFA;

        static {
            NFA nfa = new NFA(RELATION);
            DFA dfa = new DFA(nfa);
            minimizeDFA = new MinimizeDFA(dfa);
            writeRes2TXT(nfa, dfa, minimizeDFA);
        }

        private RelationRecognizer() {}

        static boolean isRelation(String word) {
            return minimizeDFA.ifCanReachFinal(word);
        }
    }

    /** relation识别器 */
    public static class KsRecognizer {
        private static final String KS = ">|<|>=|<=|==";
        private static MinimizeDFA minimizeDFA;

        static {
            NFA nfa = new NFA(KS);
            DFA dfa = new DFA(nfa);
            minimizeDFA = new MinimizeDFA(dfa);
            writeRes2TXT(nfa, dfa, minimizeDFA);
        }

        private KsRecognizer() {}

        static boolean isRelation(String word) {
            return minimizeDFA.ifCanReachFinal(word);
        }
    }

    /** kw识别器 */
    private static class KwRecognizer {
        private static final String KW = "void|main|int|char|float|String|if|else|while|for";
        private static MinimizeDFA minimizeDFA;

        static {
            NFA nfa = new NFA(KW);
            DFA dfa = new DFA(nfa);
            minimizeDFA = new MinimizeDFA(dfa);
            writeRes2TXT(nfa, dfa, minimizeDFA);
        }

        private KwRecognizer() {}

        static boolean isKw(String word) {
            return minimizeDFA.ifCanReachFinal(word);
        }
    }
}
